﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Connector.cs" company="dimamartovoi">
//   Apache License
//   Version 2.0, January 2004
//   http://www.apache.org/licenses
// </copyright>
// <summary>
//   Defines the Connector type.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Threading;

namespace ActorModel.Engine.Core
{
    public sealed class Connector<TState>
    {
        private readonly int id;

        private readonly Queue<TState> states = new Queue<TState>();

        private readonly int targetId;

        private readonly StateChangingNotifier<TState> notifier;

        private readonly ManualResetEvent viewStateHandle = new ManualResetEvent(true);

        public Connector(TState state, int id, int targetId, StateChangingNotifier<TState> notifier)
        {
            this.states.Enqueue(state);
            this.id = id;
            this.targetId = targetId;
            this.notifier = notifier;

            this.notifier.StateUpdated += this.ReceiveNeighborhoodStateInfo;
        }

        public event Action<ConnectorStateEventArgs<TState>> StateReceived;

        public uint ConnectorsPerNodeCount { get; set; }

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        internal TState State
        {
            get
            {
                TState state = this.states.Dequeue();
                if (this.states.Count == 0)
                {
                    this.viewStateHandle.Reset();
                }

                return state;
            }

            private set
            {
                this.states.Enqueue(value);
                this.viewStateHandle.Set();
                this.OnStateChanged(new ConnectorStateEventArgs<TState>(this.id, value));
            }
        }

        public bool HasArrivedState { get { return this.states.Count > 0; }}

        public void Send(TState state)
        {
           this.notifier.UpdateState(this.targetId, state);
        }

        public override string ToString()
        {
            this.viewStateHandle.WaitOne();

            var statesBuilder = new StringBuilder();
            foreach (TState state in this.states)
            {
                statesBuilder.AppendFormat("{0}; ", state);
            }

            return string.Format("id = [{0}], state: {1}\r\n", this.id % this.ConnectorsPerNodeCount, statesBuilder);
        }

        private void OnStateChanged(ConnectorStateEventArgs<TState> args)
        {
            Action<ConnectorStateEventArgs<TState>> handler = this.StateReceived;
            if (handler != null)
            {
                handler(args);
            }
        }

        private void ReceiveNeighborhoodStateInfo(ConnectorStateEventArgs<TState> args)
        {
            if (args.SourceId == this.id)
            {
                this.State = args.State;
            }
        }
    }
}